<!DOCTYPE html>
<html lang="en">

<head>
  <base href="https://git-scm.com/book/zh/v2/Git-%E4%B8%8E%E5%85%B6%E4%BB%96%E7%B3%BB%E7%BB%9F-%E8%BF%81%E7%A7%BB%E5%88%B0-Git" />
  <meta charset='utf-8'>
  <meta content='IE=edge,chrome=1' http-equiv='X-UA-Compatible'>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Git - 迁移到 Git</title>

  <link href='/favicon.ico' rel='shortcut icon' type='image/x-icon'>

  <link rel="stylesheet" media="screen" href="/assets/application-a058ab010d6f86a86903175bbfdaac30ef6057e5901875616753328abbf98b2c.css" />
  <script src="/assets/modernize-b3ebe0c31c24f230dc62179d3e1030d2e57a53b1668d9382c0a27dbd44a94beb.js"></script>
  <!--[if (gte IE 6)&(lte IE 8)]>
  <script src="/javascripts/selectivizr-min.js"></script>
  <![endif]-->

</head>

<body id="documentation">

  <div class="inner">
    <header>

  <a href="/"><img src="/images/logo@2x.png" width="110" height="46" alt="Git" /></a>
  <span id="tagline"></span>
  <script type="text/javascript">
    var taglines = ["fast-version-control","everything-is-local","distributed-even-if-your-workflow-isnt","local-branching-on-the-cheap","distributed-is-the-new-centralized"];
    var tagline = taglines[Math.floor(Math.random() * taglines.length)];
    document.getElementById('tagline').innerHTML = '--' + tagline;
  </script>
  <form id="search" action="/search/results">
    <input id="search-text" name="search" placeholder="Search entire site..." autocomplete="off" type="text" />
  </form>
  <div id="search-results"></div>

</header>

  </div> <!-- .inner -->

    <div class="inner">
      <div id="content-wrapper">
        <button class="sidebar-btn"></button>
<aside class="sidebar" id="sidebar">
  <nav>
    <ul>
      <li>
        <a href="/about">About</a>
        <ul class="">
          <li>
            <a href="/about">Branching and Merging</a>
          </li>
          <li>
            <a href="/about/small-and-fast">Small and Fast</a>
          </li>
          <li>
            <a href="/about/distributed">Distributed</a>
          </li>
          <li>
            <a href="/about/info-assurance">Data Assurance</a>
          </li>
          <li>
            <a href="/about/staging-area">Staging Area</a>
          </li>
          <li>
            <a href="/about/free-and-open-source">Free and Open Source</a>
          </li>
          <li>
            <a href="/about/trademark">Trademark</a>
          </li>
        </ul>
      </li>
      <li>
        <a class="active" href="/doc">Documentation</a>
        <ul class="expanded">
          <li>
            <a href="/docs">Reference</a>
          </li>
          <li>
            <a class="active" href="/book">Book</a>
          </li>
          <li>
            <a href="/videos">Videos</a>
          </li>
          <li>
            <a href="/doc/ext">External Links</a>
          </li>
        </ul>
      </li>
      <li>
        <a href="/downloads">Downloads</a>
        <ul class="">
          <li>
            <a href="/downloads/guis">GUI Clients</a>
          </li>
          <li>
            <a href="/downloads/logos">Logos</a>
          </li>
        </ul>
      </li>
      <li>
        <a href="/community">Community</a>
      </li>
    </ul>
      <hr class="sidebar">
        <p>
This book is available in
  <a href="/book/en">English</a>.
</p>
<p>
  Full translation available in
  <table>
    <tr><td><a href="/book/az">azərbaycan dili</a>,</td></tr>
    <tr><td><a href="/book/bg">български език</a>,</td></tr>
    <tr><td><a href="/book/de">Deutsch</a>,</td></tr>
    <tr><td><a href="/book/es">Español</a>,</td></tr>
    <tr><td><a href="/book/fr">Français</a>,</td></tr>
    <tr><td><a href="/book/gr">Ελληνικά</a>,</td></tr>
    <tr><td><a href="/book/ja">日本語</a>,</td></tr>
    <tr><td><a href="/book/ko">한국어</a>,</td></tr>
    <tr><td><a href="/book/nl">Nederlands</a>,</td></tr>
    <tr><td><a href="/book/ru">Русский</a>,</td></tr>
    <tr><td><a href="/book/sl">Slovenščina</a>,</td></tr>
    <tr><td><a href="/book/tl">Tagalog</a>,</td></tr>
    <tr><td><a href="/book/uk">Українська</a></td></tr>
    <tr><td><a href="/book/zh">简体中文</a>,</td></tr>
  </table>
</p>
<p>
  Partial translations available in
  <table>
    <tr><td><a href="/book/cs">Čeština</a>,</td></tr>
    <tr><td><a href="/book/mk">Македонски</a>,</td></tr>
    <tr><td><a href="/book/pl">Polski</a>,</td></tr>
    <tr><td><a href="/book/sr">Српски</a>,</td></tr>
    <tr><td><a href="/book/uz">Ўзбекча</a>,</td></tr>
    <tr><td><a href="/book/zh-tw">繁體中文</a>,</td></tr>
  </table>
</p>
<p>
  Translations started for
  <table>
    <tr><td><a href="/book/be">Беларуская</a>,</td></tr>
    <tr><td><a href="/book/fa" dir="rtl">فارسی</a>,</td></tr>
    <tr><td><a href="/book/id">Indonesian</a>,</td></tr>
    <tr><td><a href="/book/it">Italiano</a>,</td></tr>
    <tr><td><a href="/book/ms">Bahasa Melayu</a>,</td></tr>
    <tr><td><a href="/book/pt-br">Português (Brasil)</a>,</td></tr>
    <tr><td><a href="/book/pt-pt">Português (Portugal)</a>,</td></tr>
    <tr><td><a href="/book/sv">Svenska</a>,</td></tr>
    <tr><td><a href="/book/tr">Türkçe</a>.</td></tr>
  </table>
</p>
<hr class="sidebar"/>
<p>
The source of this book is  <a href="https://github.com/progit/progit2-zh">hosted on GitHub.</a></br>
Patches, suggestions and comments are welcome.
</p>


  </nav>
</aside>

        <div id="content">
          

<div id='book-chapters'>
  <a class="dropdown-trigger" id="book-chapters-trigger" data-panel-id="chapters-dropdown" href="#">Chapters ▾</a>
<div class='dropdown-panel' id='chapters-dropdown'>
  <div class="three-column">
    <div class='column-left'>
      <ol class='book-toc'>
  <li class='chapter'>
  <h2>1. <a href="/book/zh/v2/%E8%B5%B7%E6%AD%A5-%E5%85%B3%E4%BA%8E%E7%89%88%E6%9C%AC%E6%8E%A7%E5%88%B6">起步</a></h2>
    <ol>
        <li>
          1.1
          <a href="/book/zh/v2/%E8%B5%B7%E6%AD%A5-%E5%85%B3%E4%BA%8E%E7%89%88%E6%9C%AC%E6%8E%A7%E5%88%B6" >关于版本控制 </a>
        </li>
        <li>
          1.2
          <a href="/book/zh/v2/%E8%B5%B7%E6%AD%A5-Git-%E7%AE%80%E5%8F%B2" >Git 简史 </a>
        </li>
        <li>
          1.3
          <a href="/book/zh/v2/%E8%B5%B7%E6%AD%A5-Git-%E6%98%AF%E4%BB%80%E4%B9%88%EF%BC%9F" >Git 是什么？ </a>
        </li>
        <li>
          1.4
          <a href="/book/zh/v2/%E8%B5%B7%E6%AD%A5-%E5%91%BD%E4%BB%A4%E8%A1%8C" >命令行 </a>
        </li>
        <li>
          1.5
          <a href="/book/zh/v2/%E8%B5%B7%E6%AD%A5-%E5%AE%89%E8%A3%85-Git" >安装 Git </a>
        </li>
        <li>
          1.6
          <a href="/book/zh/v2/%E8%B5%B7%E6%AD%A5-%E5%88%9D%E6%AC%A1%E8%BF%90%E8%A1%8C-Git-%E5%89%8D%E7%9A%84%E9%85%8D%E7%BD%AE" >初次运行 Git 前的配置 </a>
        </li>
        <li>
          1.7
          <a href="/book/zh/v2/%E8%B5%B7%E6%AD%A5-%E8%8E%B7%E5%8F%96%E5%B8%AE%E5%8A%A9" >获取帮助 </a>
        </li>
        <li>
          1.8
          <a href="/book/zh/v2/%E8%B5%B7%E6%AD%A5-%E6%80%BB%E7%BB%93" >总结 </a>
        </li>
    </ol>
  </li>
  <li class='chapter'>
  <h2>2. <a href="/book/zh/v2/Git-%E5%9F%BA%E7%A1%80-%E8%8E%B7%E5%8F%96-Git-%E4%BB%93%E5%BA%93">Git 基础</a></h2>
    <ol>
        <li>
          2.1
          <a href="/book/zh/v2/Git-%E5%9F%BA%E7%A1%80-%E8%8E%B7%E5%8F%96-Git-%E4%BB%93%E5%BA%93" >获取 Git 仓库 </a>
        </li>
        <li>
          2.2
          <a href="/book/zh/v2/Git-%E5%9F%BA%E7%A1%80-%E8%AE%B0%E5%BD%95%E6%AF%8F%E6%AC%A1%E6%9B%B4%E6%96%B0%E5%88%B0%E4%BB%93%E5%BA%93" >记录每次更新到仓库 </a>
        </li>
        <li>
          2.3
          <a href="/book/zh/v2/Git-%E5%9F%BA%E7%A1%80-%E6%9F%A5%E7%9C%8B%E6%8F%90%E4%BA%A4%E5%8E%86%E5%8F%B2" >查看提交历史 </a>
        </li>
        <li>
          2.4
          <a href="/book/zh/v2/Git-%E5%9F%BA%E7%A1%80-%E6%92%A4%E6%B6%88%E6%93%8D%E4%BD%9C" >撤消操作 </a>
        </li>
        <li>
          2.5
          <a href="/book/zh/v2/Git-%E5%9F%BA%E7%A1%80-%E8%BF%9C%E7%A8%8B%E4%BB%93%E5%BA%93%E7%9A%84%E4%BD%BF%E7%94%A8" >远程仓库的使用 </a>
        </li>
        <li>
          2.6
          <a href="/book/zh/v2/Git-%E5%9F%BA%E7%A1%80-%E6%89%93%E6%A0%87%E7%AD%BE" >打标签 </a>
        </li>
        <li>
          2.7
          <a href="/book/zh/v2/Git-%E5%9F%BA%E7%A1%80-Git-%E5%88%AB%E5%90%8D" >Git 别名 </a>
        </li>
        <li>
          2.8
          <a href="/book/zh/v2/Git-%E5%9F%BA%E7%A1%80-%E6%80%BB%E7%BB%93" >总结 </a>
        </li>
    </ol>
  </li>
  <li class='chapter'>
  <h2>3. <a href="/book/zh/v2/Git-%E5%88%86%E6%94%AF-%E5%88%86%E6%94%AF%E7%AE%80%E4%BB%8B">Git 分支</a></h2>
    <ol>
        <li>
          3.1
          <a href="/book/zh/v2/Git-%E5%88%86%E6%94%AF-%E5%88%86%E6%94%AF%E7%AE%80%E4%BB%8B" >分支简介 </a>
        </li>
        <li>
          3.2
          <a href="/book/zh/v2/Git-%E5%88%86%E6%94%AF-%E5%88%86%E6%94%AF%E7%9A%84%E6%96%B0%E5%BB%BA%E4%B8%8E%E5%90%88%E5%B9%B6" >分支的新建与合并 </a>
        </li>
        <li>
          3.3
          <a href="/book/zh/v2/Git-%E5%88%86%E6%94%AF-%E5%88%86%E6%94%AF%E7%AE%A1%E7%90%86" >分支管理 </a>
        </li>
        <li>
          3.4
          <a href="/book/zh/v2/Git-%E5%88%86%E6%94%AF-%E5%88%86%E6%94%AF%E5%BC%80%E5%8F%91%E5%B7%A5%E4%BD%9C%E6%B5%81" >分支开发工作流 </a>
        </li>
        <li>
          3.5
          <a href="/book/zh/v2/Git-%E5%88%86%E6%94%AF-%E8%BF%9C%E7%A8%8B%E5%88%86%E6%94%AF" >远程分支 </a>
        </li>
        <li>
          3.6
          <a href="/book/zh/v2/Git-%E5%88%86%E6%94%AF-%E5%8F%98%E5%9F%BA" >变基 </a>
        </li>
        <li>
          3.7
          <a href="/book/zh/v2/Git-%E5%88%86%E6%94%AF-%E6%80%BB%E7%BB%93" >总结 </a>
        </li>
    </ol>
  </li>
  <li class='chapter'>
  <h2>4. <a href="/book/zh/v2/%E6%9C%8D%E5%8A%A1%E5%99%A8%E4%B8%8A%E7%9A%84-Git-%E5%8D%8F%E8%AE%AE">服务器上的 Git</a></h2>
    <ol>
        <li>
          4.1
          <a href="/book/zh/v2/%E6%9C%8D%E5%8A%A1%E5%99%A8%E4%B8%8A%E7%9A%84-Git-%E5%8D%8F%E8%AE%AE" >协议 </a>
        </li>
        <li>
          4.2
          <a href="/book/zh/v2/%E6%9C%8D%E5%8A%A1%E5%99%A8%E4%B8%8A%E7%9A%84-Git-%E5%9C%A8%E6%9C%8D%E5%8A%A1%E5%99%A8%E4%B8%8A%E6%90%AD%E5%BB%BA-Git" >在服务器上搭建 Git </a>
        </li>
        <li>
          4.3
          <a href="/book/zh/v2/%E6%9C%8D%E5%8A%A1%E5%99%A8%E4%B8%8A%E7%9A%84-Git-%E7%94%9F%E6%88%90-SSH-%E5%85%AC%E9%92%A5" >生成 SSH 公钥 </a>
        </li>
        <li>
          4.4
          <a href="/book/zh/v2/%E6%9C%8D%E5%8A%A1%E5%99%A8%E4%B8%8A%E7%9A%84-Git-%E9%85%8D%E7%BD%AE%E6%9C%8D%E5%8A%A1%E5%99%A8" >配置服务器 </a>
        </li>
        <li>
          4.5
          <a href="/book/zh/v2/%E6%9C%8D%E5%8A%A1%E5%99%A8%E4%B8%8A%E7%9A%84-Git-Git-%E5%AE%88%E6%8A%A4%E8%BF%9B%E7%A8%8B" >Git 守护进程 </a>
        </li>
        <li>
          4.6
          <a href="/book/zh/v2/%E6%9C%8D%E5%8A%A1%E5%99%A8%E4%B8%8A%E7%9A%84-Git-Smart-HTTP" >Smart HTTP </a>
        </li>
        <li>
          4.7
          <a href="/book/zh/v2/%E6%9C%8D%E5%8A%A1%E5%99%A8%E4%B8%8A%E7%9A%84-Git-GitWeb" >GitWeb </a>
        </li>
        <li>
          4.8
          <a href="/book/zh/v2/%E6%9C%8D%E5%8A%A1%E5%99%A8%E4%B8%8A%E7%9A%84-Git-GitLab" >GitLab </a>
        </li>
        <li>
          4.9
          <a href="/book/zh/v2/%E6%9C%8D%E5%8A%A1%E5%99%A8%E4%B8%8A%E7%9A%84-Git-%E7%AC%AC%E4%B8%89%E6%96%B9%E6%89%98%E7%AE%A1%E7%9A%84%E9%80%89%E6%8B%A9" >第三方托管的选择 </a>
        </li>
        <li>
          4.10
          <a href="/book/zh/v2/%E6%9C%8D%E5%8A%A1%E5%99%A8%E4%B8%8A%E7%9A%84-Git-%E6%80%BB%E7%BB%93" >总结 </a>
        </li>
    </ol>
  </li>
  <li class='chapter'>
  <h2>5. <a href="/book/zh/v2/%E5%88%86%E5%B8%83%E5%BC%8F-Git-%E5%88%86%E5%B8%83%E5%BC%8F%E5%B7%A5%E4%BD%9C%E6%B5%81%E7%A8%8B">分布式 Git</a></h2>
    <ol>
        <li>
          5.1
          <a href="/book/zh/v2/%E5%88%86%E5%B8%83%E5%BC%8F-Git-%E5%88%86%E5%B8%83%E5%BC%8F%E5%B7%A5%E4%BD%9C%E6%B5%81%E7%A8%8B" >分布式工作流程 </a>
        </li>
        <li>
          5.2
          <a href="/book/zh/v2/%E5%88%86%E5%B8%83%E5%BC%8F-Git-%E5%90%91%E4%B8%80%E4%B8%AA%E9%A1%B9%E7%9B%AE%E8%B4%A1%E7%8C%AE" >向一个项目贡献 </a>
        </li>
        <li>
          5.3
          <a href="/book/zh/v2/%E5%88%86%E5%B8%83%E5%BC%8F-Git-%E7%BB%B4%E6%8A%A4%E9%A1%B9%E7%9B%AE" >维护项目 </a>
        </li>
        <li>
          5.4
          <a href="/book/zh/v2/%E5%88%86%E5%B8%83%E5%BC%8F-Git-%E6%80%BB%E7%BB%93" >总结 </a>
        </li>
    </ol>
  </li>
</ol>

    </div>
    <div class='column-middle'>
      <ol class='book-toc'>
  <li class='chapter'>
  <h2>6. <a href="/book/zh/v2/GitHub-%E8%B4%A6%E6%88%B7%E7%9A%84%E5%88%9B%E5%BB%BA%E5%92%8C%E9%85%8D%E7%BD%AE">GitHub</a></h2>
    <ol>
        <li>
          6.1
          <a href="/book/zh/v2/GitHub-%E8%B4%A6%E6%88%B7%E7%9A%84%E5%88%9B%E5%BB%BA%E5%92%8C%E9%85%8D%E7%BD%AE" >账户的创建和配置 </a>
        </li>
        <li>
          6.2
          <a href="/book/zh/v2/GitHub-%E5%AF%B9%E9%A1%B9%E7%9B%AE%E5%81%9A%E5%87%BA%E8%B4%A1%E7%8C%AE" >对项目做出贡献 </a>
        </li>
        <li>
          6.3
          <a href="/book/zh/v2/GitHub-%E7%BB%B4%E6%8A%A4%E9%A1%B9%E7%9B%AE" >维护项目 </a>
        </li>
        <li>
          6.4
          <a href="/book/zh/v2/GitHub-%E7%AE%A1%E7%90%86%E7%BB%84%E7%BB%87" >管理组织 </a>
        </li>
        <li>
          6.5
          <a href="/book/zh/v2/GitHub-%E8%84%9A%E6%9C%AC-GitHub" >脚本 GitHub </a>
        </li>
        <li>
          6.6
          <a href="/book/zh/v2/GitHub-%E6%80%BB%E7%BB%93" >总结 </a>
        </li>
    </ol>
  </li>
  <li class='chapter'>
  <h2>7. <a href="/book/zh/v2/Git-%E5%B7%A5%E5%85%B7-%E9%80%89%E6%8B%A9%E4%BF%AE%E8%AE%A2%E7%89%88%E6%9C%AC">Git 工具</a></h2>
    <ol>
        <li>
          7.1
          <a href="/book/zh/v2/Git-%E5%B7%A5%E5%85%B7-%E9%80%89%E6%8B%A9%E4%BF%AE%E8%AE%A2%E7%89%88%E6%9C%AC" >选择修订版本 </a>
        </li>
        <li>
          7.2
          <a href="/book/zh/v2/Git-%E5%B7%A5%E5%85%B7-%E4%BA%A4%E4%BA%92%E5%BC%8F%E6%9A%82%E5%AD%98" >交互式暂存 </a>
        </li>
        <li>
          7.3
          <a href="/book/zh/v2/Git-%E5%B7%A5%E5%85%B7-%E8%B4%AE%E8%97%8F%E4%B8%8E%E6%B8%85%E7%90%86" >贮藏与清理 </a>
        </li>
        <li>
          7.4
          <a href="/book/zh/v2/Git-%E5%B7%A5%E5%85%B7-%E7%AD%BE%E7%BD%B2%E5%B7%A5%E4%BD%9C" >签署工作 </a>
        </li>
        <li>
          7.5
          <a href="/book/zh/v2/Git-%E5%B7%A5%E5%85%B7-%E6%90%9C%E7%B4%A2" >搜索 </a>
        </li>
        <li>
          7.6
          <a href="/book/zh/v2/Git-%E5%B7%A5%E5%85%B7-%E9%87%8D%E5%86%99%E5%8E%86%E5%8F%B2" >重写历史 </a>
        </li>
        <li>
          7.7
          <a href="/book/zh/v2/Git-%E5%B7%A5%E5%85%B7-%E9%87%8D%E7%BD%AE%E6%8F%AD%E5%AF%86" >重置揭密 </a>
        </li>
        <li>
          7.8
          <a href="/book/zh/v2/Git-%E5%B7%A5%E5%85%B7-%E9%AB%98%E7%BA%A7%E5%90%88%E5%B9%B6" >高级合并 </a>
        </li>
        <li>
          7.9
          <a href="/book/zh/v2/Git-%E5%B7%A5%E5%85%B7-Rerere" >Rerere </a>
        </li>
        <li>
          7.10
          <a href="/book/zh/v2/Git-%E5%B7%A5%E5%85%B7-%E4%BD%BF%E7%94%A8-Git-%E8%B0%83%E8%AF%95" >使用 Git 调试 </a>
        </li>
        <li>
          7.11
          <a href="/book/zh/v2/Git-%E5%B7%A5%E5%85%B7-%E5%AD%90%E6%A8%A1%E5%9D%97" >子模块 </a>
        </li>
        <li>
          7.12
          <a href="/book/zh/v2/Git-%E5%B7%A5%E5%85%B7-%E6%89%93%E5%8C%85" >打包 </a>
        </li>
        <li>
          7.13
          <a href="/book/zh/v2/Git-%E5%B7%A5%E5%85%B7-%E6%9B%BF%E6%8D%A2" >替换 </a>
        </li>
        <li>
          7.14
          <a href="/book/zh/v2/Git-%E5%B7%A5%E5%85%B7-%E5%87%AD%E8%AF%81%E5%AD%98%E5%82%A8" >凭证存储 </a>
        </li>
        <li>
          7.15
          <a href="/book/zh/v2/Git-%E5%B7%A5%E5%85%B7-%E6%80%BB%E7%BB%93" >总结 </a>
        </li>
    </ol>
  </li>
  <li class='chapter'>
  <h2>8. <a href="/book/zh/v2/%E8%87%AA%E5%AE%9A%E4%B9%89-Git-%E9%85%8D%E7%BD%AE-Git">自定义 Git</a></h2>
    <ol>
        <li>
          8.1
          <a href="/book/zh/v2/%E8%87%AA%E5%AE%9A%E4%B9%89-Git-%E9%85%8D%E7%BD%AE-Git" >配置 Git </a>
        </li>
        <li>
          8.2
          <a href="/book/zh/v2/%E8%87%AA%E5%AE%9A%E4%B9%89-Git-Git-%E5%B1%9E%E6%80%A7" >Git 属性 </a>
        </li>
        <li>
          8.3
          <a href="/book/zh/v2/%E8%87%AA%E5%AE%9A%E4%B9%89-Git-Git-%E9%92%A9%E5%AD%90" >Git 钩子 </a>
        </li>
        <li>
          8.4
          <a href="/book/zh/v2/%E8%87%AA%E5%AE%9A%E4%B9%89-Git-%E4%BD%BF%E7%94%A8%E5%BC%BA%E5%88%B6%E7%AD%96%E7%95%A5%E7%9A%84%E4%B8%80%E4%B8%AA%E4%BE%8B%E5%AD%90" >使用强制策略的一个例子 </a>
        </li>
        <li>
          8.5
          <a href="/book/zh/v2/%E8%87%AA%E5%AE%9A%E4%B9%89-Git-%E6%80%BB%E7%BB%93" >总结 </a>
        </li>
    </ol>
  </li>
  <li class='chapter'>
  <h2>9. <a href="/book/zh/v2/Git-%E4%B8%8E%E5%85%B6%E4%BB%96%E7%B3%BB%E7%BB%9F-%E4%BD%9C%E4%B8%BA%E5%AE%A2%E6%88%B7%E7%AB%AF%E7%9A%84-Git">Git 与其他系统</a></h2>
    <ol>
        <li>
          9.1
          <a href="/book/zh/v2/Git-%E4%B8%8E%E5%85%B6%E4%BB%96%E7%B3%BB%E7%BB%9F-%E4%BD%9C%E4%B8%BA%E5%AE%A2%E6%88%B7%E7%AB%AF%E7%9A%84-Git" >作为客户端的 Git </a>
        </li>
        <li>
          9.2
          <a href="/book/zh/v2/Git-%E4%B8%8E%E5%85%B6%E4%BB%96%E7%B3%BB%E7%BB%9F-%E8%BF%81%E7%A7%BB%E5%88%B0-Git" class=active>迁移到 Git </a>
        </li>
        <li>
          9.3
          <a href="/book/zh/v2/Git-%E4%B8%8E%E5%85%B6%E4%BB%96%E7%B3%BB%E7%BB%9F-%E6%80%BB%E7%BB%93" >总结 </a>
        </li>
    </ol>
  </li>
  <li class='chapter'>
  <h2>10. <a href="/book/zh/v2/Git-%E5%86%85%E9%83%A8%E5%8E%9F%E7%90%86-%E5%BA%95%E5%B1%82%E5%91%BD%E4%BB%A4%E4%B8%8E%E4%B8%8A%E5%B1%82%E5%91%BD%E4%BB%A4">Git 内部原理</a></h2>
    <ol>
        <li>
          10.1
          <a href="/book/zh/v2/Git-%E5%86%85%E9%83%A8%E5%8E%9F%E7%90%86-%E5%BA%95%E5%B1%82%E5%91%BD%E4%BB%A4%E4%B8%8E%E4%B8%8A%E5%B1%82%E5%91%BD%E4%BB%A4" >底层命令与上层命令 </a>
        </li>
        <li>
          10.2
          <a href="/book/zh/v2/Git-%E5%86%85%E9%83%A8%E5%8E%9F%E7%90%86-Git-%E5%AF%B9%E8%B1%A1" >Git 对象 </a>
        </li>
        <li>
          10.3
          <a href="/book/zh/v2/Git-%E5%86%85%E9%83%A8%E5%8E%9F%E7%90%86-Git-%E5%BC%95%E7%94%A8" >Git 引用 </a>
        </li>
        <li>
          10.4
          <a href="/book/zh/v2/Git-%E5%86%85%E9%83%A8%E5%8E%9F%E7%90%86-%E5%8C%85%E6%96%87%E4%BB%B6" >包文件 </a>
        </li>
        <li>
          10.5
          <a href="/book/zh/v2/Git-%E5%86%85%E9%83%A8%E5%8E%9F%E7%90%86-%E5%BC%95%E7%94%A8%E8%A7%84%E8%8C%83" >引用规范 </a>
        </li>
        <li>
          10.6
          <a href="/book/zh/v2/Git-%E5%86%85%E9%83%A8%E5%8E%9F%E7%90%86-%E4%BC%A0%E8%BE%93%E5%8D%8F%E8%AE%AE" >传输协议 </a>
        </li>
        <li>
          10.7
          <a href="/book/zh/v2/Git-%E5%86%85%E9%83%A8%E5%8E%9F%E7%90%86-%E7%BB%B4%E6%8A%A4%E4%B8%8E%E6%95%B0%E6%8D%AE%E6%81%A2%E5%A4%8D" >维护与数据恢复 </a>
        </li>
        <li>
          10.8
          <a href="/book/zh/v2/Git-%E5%86%85%E9%83%A8%E5%8E%9F%E7%90%86-%E7%8E%AF%E5%A2%83%E5%8F%98%E9%87%8F" >环境变量 </a>
        </li>
        <li>
          10.9
          <a href="/book/zh/v2/Git-%E5%86%85%E9%83%A8%E5%8E%9F%E7%90%86-%E6%80%BB%E7%BB%93" >总结 </a>
        </li>
    </ol>
  </li>
</ol>

    </div>
    <div class='column-right'>
      <ol class='book-toc'>
  <li class='chapter'>
  <h2>A1. <a href="/book/zh/v2/%E9%99%84%E5%BD%95-A%3A-%E5%9C%A8%E5%85%B6%E5%AE%83%E7%8E%AF%E5%A2%83%E4%B8%AD%E4%BD%BF%E7%94%A8-Git-%E5%9B%BE%E5%BD%A2%E7%95%8C%E9%9D%A2">附录 A: 在其它环境中使用 Git</a></h2>
    <ol>
        <li>
          A1.1
          <a href="/book/zh/v2/%E9%99%84%E5%BD%95-A%3A-%E5%9C%A8%E5%85%B6%E5%AE%83%E7%8E%AF%E5%A2%83%E4%B8%AD%E4%BD%BF%E7%94%A8-Git-%E5%9B%BE%E5%BD%A2%E7%95%8C%E9%9D%A2" >图形界面 </a>
        </li>
        <li>
          A1.2
          <a href="/book/zh/v2/%E9%99%84%E5%BD%95-A%3A-%E5%9C%A8%E5%85%B6%E5%AE%83%E7%8E%AF%E5%A2%83%E4%B8%AD%E4%BD%BF%E7%94%A8-Git-Visual-Studio-%E4%B8%AD%E7%9A%84-Git" >Visual Studio 中的 Git </a>
        </li>
        <li>
          A1.3
          <a href="/book/zh/v2/%E9%99%84%E5%BD%95-A%3A-%E5%9C%A8%E5%85%B6%E5%AE%83%E7%8E%AF%E5%A2%83%E4%B8%AD%E4%BD%BF%E7%94%A8-Git-Visual-Studio-Code-%E4%B8%AD%E7%9A%84-Git" >Visual Studio Code 中的 Git </a>
        </li>
        <li>
          A1.4
          <a href="/book/zh/v2/%E9%99%84%E5%BD%95-A%3A-%E5%9C%A8%E5%85%B6%E5%AE%83%E7%8E%AF%E5%A2%83%E4%B8%AD%E4%BD%BF%E7%94%A8-Git-Eclipse-%E4%B8%AD%E7%9A%84-Git" >Eclipse 中的 Git </a>
        </li>
        <li>
          A1.5
          <a href="/book/zh/v2/%E9%99%84%E5%BD%95-A%3A-%E5%9C%A8%E5%85%B6%E5%AE%83%E7%8E%AF%E5%A2%83%E4%B8%AD%E4%BD%BF%E7%94%A8-Git-IntelliJ-%2F-PyCharm-%2F-WebStorm-%2F-PhpStorm-%2F-RubyMine-%E4%B8%AD%E7%9A%84-Git" >IntelliJ / PyCharm / WebStorm / PhpStorm / RubyMine 中的 Git </a>
        </li>
        <li>
          A1.6
          <a href="/book/zh/v2/%E9%99%84%E5%BD%95-A%3A-%E5%9C%A8%E5%85%B6%E5%AE%83%E7%8E%AF%E5%A2%83%E4%B8%AD%E4%BD%BF%E7%94%A8-Git-Sublime-Text-%E4%B8%AD%E7%9A%84-Git" >Sublime Text 中的 Git </a>
        </li>
        <li>
          A1.7
          <a href="/book/zh/v2/%E9%99%84%E5%BD%95-A%3A-%E5%9C%A8%E5%85%B6%E5%AE%83%E7%8E%AF%E5%A2%83%E4%B8%AD%E4%BD%BF%E7%94%A8-Git-Bash-%E4%B8%AD%E7%9A%84-Git" >Bash 中的 Git </a>
        </li>
        <li>
          A1.8
          <a href="/book/zh/v2/%E9%99%84%E5%BD%95-A%3A-%E5%9C%A8%E5%85%B6%E5%AE%83%E7%8E%AF%E5%A2%83%E4%B8%AD%E4%BD%BF%E7%94%A8-Git-Zsh-%E4%B8%AD%E7%9A%84-Git" >Zsh 中的 Git </a>
        </li>
        <li>
          A1.9
          <a href="/book/zh/v2/%E9%99%84%E5%BD%95-A%3A-%E5%9C%A8%E5%85%B6%E5%AE%83%E7%8E%AF%E5%A2%83%E4%B8%AD%E4%BD%BF%E7%94%A8-Git-Git-%E5%9C%A8-PowerShell-%E4%B8%AD%E4%BD%BF%E7%94%A8-Git" >Git 在 PowerShell 中使用 Git </a>
        </li>
        <li>
          A1.10
          <a href="/book/zh/v2/%E9%99%84%E5%BD%95-A%3A-%E5%9C%A8%E5%85%B6%E5%AE%83%E7%8E%AF%E5%A2%83%E4%B8%AD%E4%BD%BF%E7%94%A8-Git-%E6%80%BB%E7%BB%93" >总结 </a>
        </li>
    </ol>
  </li>
  <li class='chapter'>
  <h2>A2. <a href="/book/zh/v2/%E9%99%84%E5%BD%95-B%3A-%E5%9C%A8%E4%BD%A0%E7%9A%84%E5%BA%94%E7%94%A8%E4%B8%AD%E5%B5%8C%E5%85%A5-Git-%E5%91%BD%E4%BB%A4%E8%A1%8C-Git-%E6%96%B9%E5%BC%8F">附录 B: 在你的应用中嵌入 Git</a></h2>
    <ol>
        <li>
          A2.1
          <a href="/book/zh/v2/%E9%99%84%E5%BD%95-B%3A-%E5%9C%A8%E4%BD%A0%E7%9A%84%E5%BA%94%E7%94%A8%E4%B8%AD%E5%B5%8C%E5%85%A5-Git-%E5%91%BD%E4%BB%A4%E8%A1%8C-Git-%E6%96%B9%E5%BC%8F" >命令行 Git 方式 </a>
        </li>
        <li>
          A2.2
          <a href="/book/zh/v2/%E9%99%84%E5%BD%95-B%3A-%E5%9C%A8%E4%BD%A0%E7%9A%84%E5%BA%94%E7%94%A8%E4%B8%AD%E5%B5%8C%E5%85%A5-Git-Libgit2" >Libgit2 </a>
        </li>
        <li>
          A2.3
          <a href="/book/zh/v2/%E9%99%84%E5%BD%95-B%3A-%E5%9C%A8%E4%BD%A0%E7%9A%84%E5%BA%94%E7%94%A8%E4%B8%AD%E5%B5%8C%E5%85%A5-Git-JGit" >JGit </a>
        </li>
        <li>
          A2.4
          <a href="/book/zh/v2/%E9%99%84%E5%BD%95-B%3A-%E5%9C%A8%E4%BD%A0%E7%9A%84%E5%BA%94%E7%94%A8%E4%B8%AD%E5%B5%8C%E5%85%A5-Git-go-git" >go-git </a>
        </li>
        <li>
          A2.5
          <a href="/book/zh/v2/%E9%99%84%E5%BD%95-B%3A-%E5%9C%A8%E4%BD%A0%E7%9A%84%E5%BA%94%E7%94%A8%E4%B8%AD%E5%B5%8C%E5%85%A5-Git-Dulwich" >Dulwich </a>
        </li>
    </ol>
  </li>
  <li class='chapter'>
  <h2>A3. <a href="/book/zh/v2/%E9%99%84%E5%BD%95-C%3A-Git-%E5%91%BD%E4%BB%A4-%E8%AE%BE%E7%BD%AE%E4%B8%8E%E9%85%8D%E7%BD%AE">附录 C: Git 命令</a></h2>
    <ol>
        <li>
          A3.1
          <a href="/book/zh/v2/%E9%99%84%E5%BD%95-C%3A-Git-%E5%91%BD%E4%BB%A4-%E8%AE%BE%E7%BD%AE%E4%B8%8E%E9%85%8D%E7%BD%AE" >设置与配置 </a>
        </li>
        <li>
          A3.2
          <a href="/book/zh/v2/%E9%99%84%E5%BD%95-C%3A-Git-%E5%91%BD%E4%BB%A4-%E8%8E%B7%E5%8F%96%E4%B8%8E%E5%88%9B%E5%BB%BA%E9%A1%B9%E7%9B%AE" >获取与创建项目 </a>
        </li>
        <li>
          A3.3
          <a href="/book/zh/v2/%E9%99%84%E5%BD%95-C%3A-Git-%E5%91%BD%E4%BB%A4-%E5%BF%AB%E7%85%A7%E5%9F%BA%E7%A1%80" >快照基础 </a>
        </li>
        <li>
          A3.4
          <a href="/book/zh/v2/%E9%99%84%E5%BD%95-C%3A-Git-%E5%91%BD%E4%BB%A4-%E5%88%86%E6%94%AF%E4%B8%8E%E5%90%88%E5%B9%B6" >分支与合并 </a>
        </li>
        <li>
          A3.5
          <a href="/book/zh/v2/%E9%99%84%E5%BD%95-C%3A-Git-%E5%91%BD%E4%BB%A4-%E9%A1%B9%E7%9B%AE%E5%88%86%E4%BA%AB%E4%B8%8E%E6%9B%B4%E6%96%B0" >项目分享与更新 </a>
        </li>
        <li>
          A3.6
          <a href="/book/zh/v2/%E9%99%84%E5%BD%95-C%3A-Git-%E5%91%BD%E4%BB%A4-%E6%A3%80%E6%9F%A5%E4%B8%8E%E6%AF%94%E8%BE%83" >检查与比较 </a>
        </li>
        <li>
          A3.7
          <a href="/book/zh/v2/%E9%99%84%E5%BD%95-C%3A-Git-%E5%91%BD%E4%BB%A4-%E8%B0%83%E8%AF%95" >调试 </a>
        </li>
        <li>
          A3.8
          <a href="/book/zh/v2/%E9%99%84%E5%BD%95-C%3A-Git-%E5%91%BD%E4%BB%A4-%E8%A1%A5%E4%B8%81" >补丁 </a>
        </li>
        <li>
          A3.9
          <a href="/book/zh/v2/%E9%99%84%E5%BD%95-C%3A-Git-%E5%91%BD%E4%BB%A4-%E9%82%AE%E4%BB%B6" >邮件 </a>
        </li>
        <li>
          A3.10
          <a href="/book/zh/v2/%E9%99%84%E5%BD%95-C%3A-Git-%E5%91%BD%E4%BB%A4-%E5%A4%96%E9%83%A8%E7%B3%BB%E7%BB%9F" >外部系统 </a>
        </li>
        <li>
          A3.11
          <a href="/book/zh/v2/%E9%99%84%E5%BD%95-C%3A-Git-%E5%91%BD%E4%BB%A4-%E7%AE%A1%E7%90%86" >管理 </a>
        </li>
        <li>
          A3.12
          <a href="/book/zh/v2/%E9%99%84%E5%BD%95-C%3A-Git-%E5%91%BD%E4%BB%A4-%E5%BA%95%E5%B1%82%E5%91%BD%E4%BB%A4" >底层命令 </a>
        </li>
    </ol>
  </li>
</ol>

    </div>
  </div>
 </div>

    <span class="light" id="edition">
      2nd Edition
    </span>
</div>

<div id='main' class="book edition2">
    <h1>9.2 Git 与其他系统 - 迁移到 Git</h1>
  <div>
<h2 id="_migrating">迁移到 Git</h2>
<div class="paragraph">
<p>
如果你现在有一个正在使用其他 VCS 的代码库，但是你已经决定开始使用 Git，必须通过某种方式将你的项目迁移至 Git。
这一部分会介绍一些通用系统的导入器，然后演示如何开发你自己定制的导入器。
你将会学习如何从几个大型专业应用的 SCM 系统中导入数据，不仅因为它们是大多数想要转换的用户正在使用的系统，也因为获取针对它们的高质量工具很容易。</p>
</div>
<div class="sect3">
<h3 id="_subversion">Subversion</h3>
<div class="paragraph">
<p>

如果你阅读过前面关于 <code>git svn</code> 的章节，可以轻松地使用那些指令来 <code>git svn clone</code> 一个仓库，停止使用 Subversion 服务器，推送到一个新的 Git 服务器，然后就可以开始使用了。
如果你想要历史，可以从 Subversion 服务器上尽可能快地拉取数据来完成这件事（这可能会花费一些时间）。</p>
</div>
<div class="paragraph">
<p>然而，导入并不完美；因为花费太长时间了，你可能早已用其他方法完成导入操作。
导入产生的第一个问题就是作者信息。
在 Subversion 中，每一个人提交时都需要在系统中有一个用户，它会被记录在提交信息内。
在之前章节的例子中几个地方显示了 <code>schacon</code>，比如 <code>blame</code> 输出与 <code>git svn log</code>。
如果想要将上面的 Subversion 用户映射到一个更好的 Git 作者数据中，你需要一个 Subversion 用户到 Git 用户的映射。
创建一个 <code>users.txt</code> 的文件包含像下面这种格式的映射：</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code>schacon = Scott Chacon &lt;schacon@geemail.com&gt;
selse = Someo Nelse &lt;selse@geemail.com&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>为了获得 SVN 使用的作者名字列表，可以运行这个：</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-console" data-lang="console">$ svn log --xml --quiet | grep author | sort -u | \
  perl -pe 's/.*&gt;(.*?)&lt;.*/$1 = /'</code></pre>
</div>
</div>
<div class="paragraph">
<p>这会将日志输出为 XML 格式，然后保留作者信息行、去除重复、去除 XML 标记。
很显然这只会在安装了 <code>grep</code>、<code>sort</code> 与 <code>perl</code> 的机器上运行。
然后，将输出重定向到你的 <code>users.txt</code> 文件中，这样就可以在每一个记录后面加入对应的 Git 用户数据。</p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<div class="title">Note</div>
</td>
<td class="content">
<div class="paragraph">
<p>如果你在 Windows 上运行它，那么到这里就会遇到问题。微软提供了一些不错的建议和示例：
<a href="https://docs.microsoft.com/en-us/azure/devops/repos/git/perform-migration-from-svn-to-git" class="bare">https://docs.microsoft.com/en-us/azure/devops/repos/git/perform-migration-from-svn-to-git</a>.</p>
</div>
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>你可以将此文件提供给 <code>git svn</code> 来帮助它更加精确地映射作者数据。
也可以通过传递 <code>--no-metadata</code> 给 <code>clone</code> 与 <code>init</code> 命令，告诉 <code>git svn</code> 不要包括
Subversion 通常会导入的元数据。在导入过程中，Git 会在每个提交说明的元数据中生成一个
<code>git-svn-id</code>。</p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<div class="title">Note</div>
</td>
<td class="content">
<div class="paragraph">
<p>当你想要将 Git 仓库中的提交镜像回原 SVN 仓库中时，需要保留元数据。
如果你不想在提交记录中同步它，请直接省略掉 <code>--no-metadata</code> 选项。</p>
</div>
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>这会使你的 <code>import</code> 命令看起来像这样：</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-console" data-lang="console">$ git svn clone http://my-project.googlecode.com/svn/ \
      --authors-file=users.txt --no-metadata --prefix "" -s my_project
$ cd my_project</code></pre>
</div>
</div>
<div class="paragraph">
<p>现在在 <code>my_project</code> 目录中应当有了一个更好的 Subversion 导入。
并不像是下面这样的提交：</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code>commit 37efa680e8473b615de980fa935944215428a35a
Author: schacon &lt;schacon@4c93b258-373f-11de-be05-5f7a86268029&gt;
Date:   Sun May 3 00:12:22 2009 +0000

    fixed install - go to trunk

    git-svn-id: https://my-project.googlecode.com/svn/trunk@94 4c93b258-373f-11de-
    be05-5f7a86268029</code></pre>
</div>
</div>
<div class="paragraph">
<p>反而它们看起来像是这样：</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code>commit 03a8785f44c8ea5cdb0e8834b7c8e6c469be2ff2
Author: Scott Chacon &lt;schacon@geemail.com&gt;
Date:   Sun May 3 00:12:22 2009 +0000

    fixed install - go to trunk</code></pre>
</div>
</div>
<div class="paragraph">
<p>不仅是 Author 字段更好看了，<code>git-svn-id</code> 也不在了。</p>
</div>
<div class="paragraph">
<p>之后，你应当做一些导入后的清理工作。
第一步，你应当清理 <code>git svn</code> 设置的奇怪的引用。
首先移动标签，这样它们就是标签而不是奇怪的远程引用，然后你会移动剩余的分支这样它们就是本地的了。</p>
</div>
<div class="paragraph">
<p>为了将标签变为合适的 Git 标签，运行：</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-console" data-lang="console">$ for t in $(git for-each-ref --format='%(refname:short)' refs/remotes/tags); do git tag ${t/tags\//} $t &amp;&amp; git branch -D -r $t; done</code></pre>
</div>
</div>
<div class="paragraph">
<p>这会使原来在 <code>refs/remotes/tags/</code> 里的远程分支引用变成真正的（轻量）标签。</p>
</div>
<div class="paragraph">
<p>接下来，将 <code>refs/remotes</code> 下剩余的引用移动为本地分支：</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-console" data-lang="console">$ for b in $(git for-each-ref --format='%(refname:short)' refs/remotes); do git branch $b refs/remotes/$b &amp;&amp; git branch -D -r $b; done</code></pre>
</div>
</div>
<div class="paragraph">
<p>你可能会看到一些额外的分支，这些分支的后缀是 <code>@xxx</code> (其中 xxx 是一个数字)，而在 Subversion 中你只会看到一个分支。这实际上是 Subversion 一个叫做“peg-revisions”的功能，Git在语法上没有与之对应的功能。因此， <code>git svn</code> 只是简单地将 SVN peg-revision 版本号添加到分支名称中，这同你在 SVN 中修改分支名称来定位一个分支的“peg-revision”是一样的。如果你对于 peg-revisions 完全不在乎，通过下面的命令可以轻易地移除他们：</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-console" data-lang="console">$ for p in $(git for-each-ref --format='%(refname:short)' | grep @); do git branch -D $p; done</code></pre>
</div>
</div>
<div class="paragraph">
<p>现在所有的旧分支都是真正的 Git 分支，并且所有的旧标签都是真正的 Git 标签。</p>
</div>
<div class="paragraph">
<p>还有最后一点东西需要清理。<code>git svn</code> 会创建一个名为 <code>trunk</code> 的额外分支，它对应于
Subversion 的默认分支，然而 <code>trunk</code> 引用和 <code>master</code> 指向同一个位置。
鉴于在 Git 中 <code>master</code> 最为常用，因此我们可以移除额外的分支：</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-console" data-lang="console">$ git branch -d trunk</code></pre>
</div>
</div>
<div class="paragraph">
<p>最后一件要做的事情是，将你的新 Git 服务器添加为远程仓库并推送到上面。
下面是一个将你的服务器添加为远程仓库的例子：</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-console" data-lang="console">$ git remote add origin git@my-git-server:myrepository.git</code></pre>
</div>
</div>
<div class="paragraph">
<p>因为想要上传所有分支与标签，你现在可以运行：</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-console" data-lang="console">$ git push origin --all
$ git push origin --tags</code></pre>
</div>
</div>
<div class="paragraph">
<p>通过以上漂亮、干净地导入操作，你的所有分支与标签都应该在新 Git 服务器上。</p>
</div>
</div>
<div class="sect3">
<h3 id="_mercurial">Mercurial</h3>
<div class="paragraph">
<p>
因为 Mercurial 与 Git 在表示版本时有着非常相似的模型，也因为 Git 拥有更加强大的灵活性，将一个仓库从 Mercurial 转换到 Git 是相当直接的，使用一个叫作“hg-fast-export”的工具，需要从这里拷贝一份：</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-console" data-lang="console">$ git clone https://github.com/frej/fast-export.git</code></pre>
</div>
</div>
<div class="paragraph">
<p>转换的第一步就是要先得到想要转换的 Mercurial 仓库的完整克隆：</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-console" data-lang="console">$ hg clone &lt;remote repo URL&gt; /tmp/hg-repo</code></pre>
</div>
</div>
<div class="paragraph">
<p>下一步就是创建一个作者映射文件。
Mercurial 对放入到变更集作者字段的内容比 Git 更宽容一些，所以这是一个清理的好机会。
只需要用到 <code>bash</code> 终端下的一行命令：</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-console" data-lang="console">$ cd /tmp/hg-repo
$ hg log | grep user: | sort | uniq | sed 's/user: *//' &gt; ../authors</code></pre>
</div>
</div>
<div class="paragraph">
<p>这会花费几秒钟，具体要看项目提交历史有多少，最终 <code>/tmp/authors</code> 文件看起来会像这样：</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code>bob
bob@localhost
bob &lt;bob@company.com&gt;
bob jones &lt;bob &lt;AT&gt; company &lt;DOT&gt; com&gt;
Bob Jones &lt;bob@company.com&gt;
Joe Smith &lt;joe@company.com&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>在这个例子中，同一个人（Bob）使用不同的名字创建变更集，其中一个实际上是正确的，
另一个完全不符合 Git 提交的规范。hg-fast-export 通过对每一行应用规则
<code>"&lt;input&gt;"="&lt;output&gt;"</code> ，将 <code>&lt;input&gt;</code> 映射到 <code>&lt;output&gt;</code> 来修正这个问题。
在 <code>&lt;input&gt;</code> 和 <code>&lt;output&gt;</code> 字符串中，所有 Python 的 <code>string_escape</code>
支持的转义序列都会被解释。如果作者映射文件中并不包含匹配的 <code>&lt;input&gt;</code>，
那么该作者将原封不动地被发送到 Git。
如果所有的用户名看起来都是正确的，那我们根本就不需要这个文件。
在本例中，我们会使文件看起来像这样：</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code>"bob"="Bob Jones &lt;bob@company.com&gt;"
"bob@localhost"="Bob Jones &lt;bob@company.com&gt;"
"bob &lt;bob@company.com&gt;"="Bob Jones &lt;bob@company.com&gt;"
"bob jones &lt;bob &lt;AT&gt; company &lt;DOT&gt; com&gt;"="Bob Jones &lt;bob@company.com&gt;"</code></pre>
</div>
</div>
<div class="paragraph">
<p>当分支和标签 Mercurial 中的名字在 Git 中不允许时，这种映射文件也可以用来重命名它们。</p>
</div>
<div class="paragraph">
<p>下一步是创建一个新的 Git 仓库，然后运行导出脚本：</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-console" data-lang="console">$ git init /tmp/converted
$ cd /tmp/converted
$ /tmp/fast-export/hg-fast-export.sh -r /tmp/hg-repo -A /tmp/authors</code></pre>
</div>
</div>
<div class="paragraph">
<p><code>-r</code> 选项告诉 hg-fast-export 去哪里寻找我们想要转换的 Mercurial 仓库，<code>-A</code>
标记告诉它在哪找到作者映射文件（分支和标签的映射文件分别通过 <code>-B</code> 和 <code>-T</code> 选项来指定）。
这个脚本会分析 Mercurial 变更集然后将它们转换成 Git“fast-import”功能（我们将在之后详细讨论）需要的脚本。
这会花一点时间（尽管它比通过网格 <em>更</em> 快），输出相当的冗长：</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-console" data-lang="console">$ /tmp/fast-export/hg-fast-export.sh -r /tmp/hg-repo -A /tmp/authors
Loaded 4 authors
master: Exporting full revision 1/22208 with 13/0/0 added/changed/removed files
master: Exporting simple delta revision 2/22208 with 1/1/0 added/changed/removed files
master: Exporting simple delta revision 3/22208 with 0/1/0 added/changed/removed files
[…]
master: Exporting simple delta revision 22206/22208 with 0/4/0 added/changed/removed files
master: Exporting simple delta revision 22207/22208 with 0/2/0 added/changed/removed files
master: Exporting thorough delta revision 22208/22208 with 3/213/0 added/changed/removed files
Exporting tag [0.4c] at [hg r9] [git :10]
Exporting tag [0.4d] at [hg r16] [git :17]
[…]
Exporting tag [3.1-rc] at [hg r21926] [git :21927]
Exporting tag [3.1] at [hg r21973] [git :21974]
Issued 22315 commands
git-fast-import statistics:
---------------------------------------------------------------------
Alloc'd objects:     120000
Total objects:       115032 (    208171 duplicates                  )
      blobs  :        40504 (    205320 duplicates      26117 deltas of      39602 attempts)
      trees  :        52320 (      2851 duplicates      47467 deltas of      47599 attempts)
      commits:        22208 (         0 duplicates          0 deltas of          0 attempts)
      tags   :            0 (         0 duplicates          0 deltas of          0 attempts)
Total branches:         109 (         2 loads     )
      marks:        1048576 (     22208 unique    )
      atoms:           1952
Memory total:          7860 KiB
       pools:          2235 KiB
     objects:          5625 KiB
---------------------------------------------------------------------
pack_report: getpagesize()            =       4096
pack_report: core.packedGitWindowSize = 1073741824
pack_report: core.packedGitLimit      = 8589934592
pack_report: pack_used_ctr            =      90430
pack_report: pack_mmap_calls          =      46771
pack_report: pack_open_windows        =          1 /          1
pack_report: pack_mapped              =  340852700 /  340852700
---------------------------------------------------------------------

$ git shortlog -sn
   369  Bob Jones
   365  Joe Smith</code></pre>
</div>
</div>
<div class="paragraph">
<p>那看起来非常好。
所有 Mercurial 标签都已被转换成 Git 标签，Mercurial 分支与书签都被转换成 Git 分支。
现在已经准备好将仓库推送到新的服务器那边：</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-console" data-lang="console">$ git remote add origin git@my-git-server:myrepository.git
$ git push origin --all</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h3 id="_bazaar">Bazaar</h3>
<div class="paragraph">
<p></p>
</div>
<div class="paragraph">
<p>Bazaar 是一个和 Git 非常类似的分布式版本控制系统（DVCS)，因此将 Bazzar 仓库转换成 Git 仓库是非常简单易懂的。想要完成转换，你需要安装 <code>bzr-fastimport</code> 插件。</p>
</div>
<div class="sect4">
<h4 id="_安装_bzr_fastimport_插件">安装 bzr-fastimport 插件</h4>
<div class="paragraph">
<p>安装 fastimport 插件的步骤在类 UNIX 操作系统和 Windows 上是不一样的。在类 UNIX 系统上，最简单的办法就是安装 <code>bzr-fastimport</code> 包，这种方法将会自动安装所有需要的依赖。</p>
</div>
<div class="paragraph">
<p>例如，使用 Debian 及其派生系统，你只需要进行以下操作：</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-console" data-lang="console">$ sudo apt-get install bzr-fastimport</code></pre>
</div>
</div>
<div class="paragraph">
<p>红帽企业版系统（RHEL），使用以下命令：</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-console" data-lang="console">$ sudo yum install bzr-fastimport</code></pre>
</div>
</div>
<div class="paragraph">
<p>Fedora 从 22 版本开始，采用了新的包管理器 dnf，使用以下命令：</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-console" data-lang="console">$ sudo dnf install bzr-fastimport</code></pre>
</div>
</div>
<div class="paragraph">
<p>如果直接安装包的方法不行，你可能需要使用安装插件的方法：</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-console" data-lang="console">$ mkdir --parents ~/.bazaar/plugins     # 为插件创建必要的文件夹
$ cd ~/.bazaar/plugins
$ bzr branch lp:bzr-fastimport fastimport   # 引入 fastimport 插件
$ cd fastimport
$ sudo python setup.py install --record=files.txt   # 安装插件</code></pre>
</div>
</div>
<div class="paragraph">
<p>为了确保插件工作，你同时也需要安装有 <code>fastimport</code> 这一 Python 模块。使用下面的命令可以检查这一模块安装与否，如果没有则安装这一模块：</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-console" data-lang="console">$ python -c "import fastimport"
Traceback (most recent call last):
  File "&lt;string&gt;", line 1, in &lt;module&gt;
ImportError: No module named fastimport
$ pip install fastimport</code></pre>
</div>
</div>
<div class="paragraph">
<p>如果上面的命令安装失败，你可以直接到这个地址下载 <a href="https://pypi.python.org/pypi/fastimport/" class="bare">https://pypi.python.org/pypi/fastimport/</a> 。</p>
</div>
<div class="paragraph">
<p>在 Windows 上，<code>bzr-fastimport</code> 插件在 Git 使用脱机安装并保持默认安装选项不变（可选框全部选中）的情况下是自动安装的。在这种情况下，你什么都不用做。</p>
</div>
<div class="paragraph">
<p>接下来，导入 Bazaar 仓库的方法根据你的仓库是有一个分支还是有多个分支而不同。</p>
</div>
</div>
<div class="sect4">
<h4 id="_单分支项目">单分支项目</h4>
<div class="paragraph">
<p><code>cd</code> 到包含你的 Bazaar 仓库的路径，然后初始化 Git 仓库：</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-console" data-lang="console">$ cd /path/to/the/bzr/repository
$ git init</code></pre>
</div>
</div>
<div class="paragraph">
<p>现在你可以使用以下命令轻松地导出你的 Bazaar 仓库并把它转化成 Git 仓库：</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-console" data-lang="console">$ bzr fast-export --plain . | git fast-import</code></pre>
</div>
</div>
<div class="paragraph">
<p>根据项目的大小，Git 仓库会在几秒钟到几分钟之间构建。</p>
</div>
</div>
<div class="sect4">
<h4 id="_多分支项目">多分支项目</h4>
<div class="paragraph">
<p>你同样也能够导入包含多个分支的 Bazaar 仓库。让我们假设你有两个分支，一个代表主分支（myProject.trunk），另一个是工作分支（myProject.work）。</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-console" data-lang="console">$ ls
myProject.trunk myProject.work</code></pre>
</div>
</div>
<div class="paragraph">
<p>创建一个 Git 仓库并 <code>cd</code> 进去：</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-console" data-lang="console">$ git init git-repo
$ cd git-repo</code></pre>
</div>
</div>
<div class="paragraph">
<p>将 <code>master</code> 分支拉入 Git：</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-console" data-lang="console">$ bzr fast-export --export-marks=../marks.bzr ../myProject.trunk | \
git fast-import --export-marks=../marks.git</code></pre>
</div>
</div>
<div class="paragraph">
<p>将工作分支拉入 Git：</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-console" data-lang="console">$ bzr fast-export --marks=../marks.bzr --git-branch=work ../myProject.work | \
git fast-import --import-marks=../marks.git --export-marks=../marks.git</code></pre>
</div>
</div>
<div class="paragraph">
<p>现在 <code>git branch</code> 会同时显示 <code>master</code> 分支和 <code>work</code> 分支。检查日志以确保它们是完整的，并删除 <code>marks.bzr</code> 和 <code>marks.git</code> 文件。</p>
</div>
</div>
<div class="sect4">
<h4 id="_同步暂存区">同步暂存区</h4>
<div class="paragraph">
<p>无论你有多少分支以及使用的导入方法如何，你的暂存区都不会与 <code>HEAD</code> 同步，并且在导入多个分支时，你的工作目录也不会同步。这种情况使用下面的命令可以轻松解决：</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-console" data-lang="console">$ git reset --hard HEAD</code></pre>
</div>
</div>
</div>
<div class="sect4">
<h4 id="_忽略被_bzrignore_文件指明忽略的文件">忽略被 .bzrignore 文件指明忽略的文件</h4>
<div class="paragraph">
<p>现在让我们看看要忽略的文件。第一件事情就是将 <code>.bzrignore</code> 重命名为 <code>.gitignore</code>。如果 <code>.bzrignore</code> 文件里面有一行或数行以“!!”或“RE:”开头的内容，你必须修改它，并且可能还要创建几个 <code>.gitignore</code> 文件，以便忽略与 Bazaar 忽略的文件完全相同的文件。</p>
</div>
<div class="paragraph">
<p>最后，你必须创建一个提交，其中包含此次迁移的修改。</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-console" data-lang="console">$ git mv .bzrignore .gitignore
$ # modify .gitignore if needed
$ git commit -am 'Migration from Bazaar to Git'</code></pre>
</div>
</div>
</div>
<div class="sect4">
<h4 id="_推送你的仓库到服务器">推送你的仓库到服务器</h4>
<div class="paragraph">
<p>终于到这一步了！
现在你可以推送仓库到它的“云端新家”了：</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-console" data-lang="console">$ git remote add origin git@my-git-server:mygitrepository.git
$ git push origin --all
$ git push origin --tags</code></pre>
</div>
</div>
<div class="paragraph">
<p>你的 Git 仓库准备就绪。</p>
</div>
</div>
</div>
<div class="sect3">
<h3 id="_perforce_import">Perforce</h3>
<div class="paragraph">
<p>
下一个将要看到导入的系统是 Perforce。
就像我们之前讨论过的，有两种方式让 Git 与 Perforce 互相通信：git-p4 与 Perforce Git Fusion。</p>
</div>
<div class="sect4">
<h4 id="_perforce_git_fusion">Perforce Git Fusion</h4>
<div class="paragraph">
<p>Git Fusion 使这个过程毫无痛苦。
只需要使用在 <a href="ch00/_p4_git_fusion">Git Fusion</a> 中讨论过的配置文件来配置你的项目设置、用户映射与分支，然后克隆整个仓库。
Git Fusion 让你处在一个看起来像是原生 Git 仓库的环境中，如果愿意的话你可以随时将它推送到一个原生 Git 托管中。
如果你喜欢的话甚至可以使用 Perforce 作为你的 Git 托管。</p>
</div>
</div>
<div class="sect4">
<h4 id="_git_p4">Git-p4</h4>
<div class="paragraph">
<p>Git-p4 也可以作为一个导入工具。
作为例子，我们将从 Perforce 公开仓库中导入 Jam 项目。
为了设置客户端，必须导出 P4PORT 环境变量指向 Perforce 仓库：</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-console" data-lang="console">$ export P4PORT=public.perforce.com:1666</code></pre>
</div>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<div class="title">Note</div>
</td>
<td class="content">
<div class="paragraph">
<p>为了继续后续步骤，需要连接到 Perforce 仓库。
在我们的例子中将会使用在 public.perforce.com 的公开仓库，但是你可以使用任何你有权限的仓库。</p>
</div>
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>
运行 <code>git p4 clone</code> 命令从 Perforce 服务器导入 Jam 项目，提供仓库、项目路径与你想要存放导入项目的路径：</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-console" data-lang="console">$ git-p4 clone //guest/perforce_software/jam@all p4import
Importing from //guest/perforce_software/jam@all into p4import
Initialized empty Git repository in /private/tmp/p4import/.git/
Import destination: refs/remotes/p4/master
Importing revision 9957 (100%)</code></pre>
</div>
</div>
<div class="paragraph">
<p>这个特定的项目只有一个分支，但是如果你在分支视图（或者说一些目录）中配置了一些分支，你可以将 <code>--detect-branches</code> 选项传递给 <code>git p4 clone</code> 来导入项目的所有分支。
查看 <a href="ch00/_git_p4_branches">分支</a> 来了解关于这点的更多信息。</p>
</div>
<div class="paragraph">
<p>此时你几乎已经完成了。
如果进入 <code>p4import</code> 目录中并运行 <code>git log</code>，可以看到你的导入工作：</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-console" data-lang="console">$ git log -2
commit e5da1c909e5db3036475419f6379f2c73710c4e6
Author: giles &lt;giles@giles@perforce.com&gt;
Date:   Wed Feb 8 03:13:27 2012 -0800

    Correction to line 355; change &lt;/UL&gt; to &lt;/OL&gt;.

    [git-p4: depot-paths = "//public/jam/src/": change = 8068]

commit aa21359a0a135dda85c50a7f7cf249e4f7b8fd98
Author: kwirth &lt;kwirth@perforce.com&gt;
Date:   Tue Jul 7 01:35:51 2009 -0800

    Fix spelling error on Jam doc page (cummulative -&gt; cumulative).

    [git-p4: depot-paths = "//public/jam/src/": change = 7304]</code></pre>
</div>
</div>
<div class="paragraph">
<p>你可以看到 <code>git-p4</code> 在每一个提交里都留下了一个标识符。
如果之后想要引用 Perforce 的修改序号的话，标识符保留在那里也是可以的。
然而，如果想要移除标识符，现在正是这么做的时候——在你开始在新仓库中工作之前。

可以使用 <code>git filter-branch</code> 将全部标识符移除。</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-console" data-lang="console">$ git filter-branch --msg-filter 'sed -e "/^\[git-p4:/d"'
Rewrite e5da1c909e5db3036475419f6379f2c73710c4e6 (125/125)
Ref 'refs/heads/master' was rewritten</code></pre>
</div>
</div>
<div class="paragraph">
<p>如果运行 <code>git log</code>，你会看到所有提交的 SHA-1 校验和都改变了，但是提交信息中不再有 <code>git-p4</code> 字符串了：</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-console" data-lang="console">$ git log -2
commit b17341801ed838d97f7800a54a6f9b95750839b7
Author: giles &lt;giles@giles@perforce.com&gt;
Date:   Wed Feb 8 03:13:27 2012 -0800

    Correction to line 355; change &lt;/UL&gt; to &lt;/OL&gt;.

commit 3e68c2e26cd89cb983eb52c024ecdfba1d6b3fff
Author: kwirth &lt;kwirth@perforce.com&gt;
Date:   Tue Jul 7 01:35:51 2009 -0800

    Fix spelling error on Jam doc page (cummulative -&gt; cumulative).</code></pre>
</div>
</div>
<div class="paragraph">
<p>现在导入已经准备好推送到你的新 Git 服务器上了。</p>
</div>
</div>
</div>
<div class="sect3">
<h3 id="_git_tfs">TFS</h3>
<div class="paragraph">
<p>
如果你的团队正在将他们的源代码管理从 TFVC 转换为 Git，你们会想要最高程度的无损转换。
这意味着，虽然我们在之前的交互章节介绍了 git-tfs 与 git-tf 两种工具，但是我们在本部分只能介绍 git-tfs，因为 git-tfs 支持分支，而使用 git-tf 代价太大。</p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<div class="title">Note</div>
</td>
<td class="content">
<div class="paragraph">
<p>这是一个单向转换。
这意味着 Git 仓库无法连接到原始的 TFVC 项目。</p>
</div>
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>第一件事是映射用户名。
TFVC 对待变更集作者字段的内容相当宽容，但是 Git 需要人类可读的名字与邮箱地址。
可以通过 <code>tf</code> 命令行客户端来获取这个信息，像这样：</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-powershell" data-lang="powershell">PS&gt; tf history $/myproject -recursive &gt; AUTHORS_TMP</code></pre>
</div>
</div>
<div class="paragraph">
<p>这会将历史中的所有变更集抓取下来并放到 AUTHORS_TMP 文件中，然后我们将会将 <code>User</code> 列（第二个）取出来。
打开文件找到列开始与结束的字符并替换，在下面的命令行中，<code>cut</code> 命令的参数 <code>11-20</code> 就是我们找到的：</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-powershell" data-lang="powershell">PS&gt; cat AUTHORS_TMP | cut -b 11-20 | tail -n+3 | sort | uniq &gt; AUTHORS</code></pre>
</div>
</div>
<div class="paragraph">
<p><code>cut</code> 命令只会保留每行中第 11 个到第 22 个字符。
<code>tail</code> 命令会跳过前两行，就是字段表头与 ASCII 风格的下划线。
所有这些的结果通过管道送到 <code>sort</code> 和 <code>uniq</code> 来去除重复，然后保存到 <code>AUTOHRS</code> 文件中。
下一步是手动的；为了让 git-tfs 有效地使用这个文件，每一行必须是这种格式：</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-text" data-lang="text">DOMAIN\username = User Name &lt;email@address.com&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>左边的部分是 TFVC 中的 “User” 字段，等号右边的部分是将被用作 Git 提交的用户名。</p>
</div>
<div class="paragraph">
<p>一旦有了这个文件，下一件事就是生成一个你需要的 TFVC 项目的完整克隆：</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-powershell" data-lang="powershell">PS&gt; git tfs clone --with-branches --authors=AUTHORS https://username.visualstudio.com/DefaultCollection $/project/Trunk project_git</code></pre>
</div>
</div>
<div class="paragraph">
<p>接下来要从提交信息底部清理 <code>git-tfs-id</code> 区块。
下面的命令会完成这个任务：</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-powershell" data-lang="powershell">PS&gt; git filter-branch -f --msg-filter 'sed "s/^git-tfs-id:.*$//g"' '--' --all</code></pre>
</div>
</div>
<div class="paragraph">
<p>那会使用 Git 终端环境中的 <code>sed</code> 命令来将所有以 “git-tfs-id:” 开头的行替换为 Git 会忽略的空白。</p>
</div>
<div class="paragraph">
<p>全部完成后，你就已经准备好去增加一个新的远程仓库，推送你所有的分支上去，然后你的团队就可以开始用 Git 工作了。</p>
</div>
</div>
<div class="sect3">
<h3 id="_custom_importer">一个自定义的导入器</h3>
<div class="paragraph">
<p>

如果你的系统不是上述中的任何一个，你需要在线查找一个导入器——针对许多其他系统有很多高质量的导入器，
包括 CVS、Clear Case、Visual Source Safe，甚至是一个档案目录。
如果没有一个工具适合你，需要一个不知名的工具，或者需要更大自由度的自定义导入过程，应当使用 <code>git fast-import</code>。
这个命令从标准输入中读取简单指令来写入特定的 Git 数据。
通过这种方式创建 Git 对象比运行原始 Git 命令或直接写入原始对象
（查看 <a href="ch00/ch10-git-internals">Git 内部原理</a> 了解更多内容）更容易些。
通过这种方式你可以编写导入脚本，从你要导入的系统中读取必要数据，然后直接打印指令到标准输出。
然后可以运行这个程序并通过 <code>git fast-import</code> 重定向管道输出。</p>
</div>
<div class="paragraph">
<p>为了快速演示，我们会写一个简单的导入器。
假设你在 <code>current</code> 工作，有时候会备份你的项目到时间标签 <code>back_YYYY_MM_DD</code> 备份目录中，你想要将这些导入到 Git 中。
目录结构看起来是这样：</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-console" data-lang="console">$ ls /opt/import_from
back_2014_01_02
back_2014_01_04
back_2014_01_14
back_2014_02_03
current</code></pre>
</div>
</div>
<div class="paragraph">
<p>为了导入一个 Git 目录，需要了解 Git 如何存储它的数据。
你可能记得，Git 在底层存储指向内容快照的提交对象的链表。
所有要做的就是告诉 <code>fast-import</code> 哪些内容是快照，哪个提交数据指向它们，以及它们进入的顺序。
你的策略是一次访问一个快照，然后用每个目录中的内容创建提交，并且将每一个提交与前一个连接起来。</p>
</div>
<div class="paragraph">
<p>如同我们在 <a href="ch00/_an_example_git_enforced_policy">使用强制策略的一个例子</a> 里做的，
我们将会使用 Ruby 写这个，因为它是我们平常工作中使用的并且它很容易读懂。
可以使用任何你熟悉的东西来非常轻松地写这个例子——它只需要将合适的信息打印到 <code>标准输出</code>。
然而，如果你在 Windows 上，这意味着需要特别注意不要引入回车符到行尾——
<code>git fast-import</code> 非常特别地只接受换行符（LF）而不是 Windows 使用的回车换行符（CRLF）。</p>
</div>
<div class="paragraph">
<p>现在开始，需要进入目标目录中并识别每一个子目录，每一个都是你要导入为提交的快照。
要进入到每个子目录中并为导出它打印必要的命令。
基本主循环像这个样子：</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-ruby" data-lang="ruby">last_mark = nil

# loop through the directories
Dir.chdir(ARGV[0]) do
  Dir.glob("*").each do |dir|
    next if File.file?(dir)

    # move into the target directory
    Dir.chdir(dir) do
      last_mark = print_export(dir, last_mark)
    end
  end
end</code></pre>
</div>
</div>
<div class="paragraph">
<p>在每个目录内运行 <code>print_export</code>，将会拿到清单并标记之前的快照，然后返回清单并标记现在的快照；通过这种方式，可以将它们合适地连接在一起。
“标记”是一个给提交标识符的 <code>fast-import</code> 术语；当你创建提交，为每一个提交赋予一个标记来将它与其他提交连接在一起。
这样，在你的 <code>print_export</code> 方法中第一件要做的事就是从目录名字生成一个标记：</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-ruby" data-lang="ruby">mark = convert_dir_to_mark(dir)</code></pre>
</div>
</div>
<div class="paragraph">
<p>可以创建一个目录的数组并使用索引做为标记，因为标记必须是一个整数。
方法类似这样：</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-ruby" data-lang="ruby">$marks = []
def convert_dir_to_mark(dir)
  if !$marks.include?(dir)
    $marks &lt;&lt; dir
  end
  ($marks.index(dir) + 1).to_s
end</code></pre>
</div>
</div>
<div class="paragraph">
<p>既然有一个整数代表你的提交，那还要给提交元数据一个日期。
因为目录名字表达了日期，所以你将会从中解析出日期。
你的 <code>print_export</code> 文件的下一行是：</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-ruby" data-lang="ruby">date = convert_dir_to_date(dir)</code></pre>
</div>
</div>
<div class="paragraph">
<p><code>convert_dir_to_date</code> 定义为：</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-ruby" data-lang="ruby">def convert_dir_to_date(dir)
  if dir == 'current'
    return Time.now().to_i
  else
    dir = dir.gsub('back_', '')
    (year, month, day) = dir.split('_')
    return Time.local(year, month, day).to_i
  end
end</code></pre>
</div>
</div>
<div class="paragraph">
<p>那会返回每一个目录日期的整数。
最后一项每个提交需要的元数据是提交者信息，它将会被硬编码在全局变量中：</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-ruby" data-lang="ruby">$author = 'John Doe &lt;john@example.com&gt;'</code></pre>
</div>
</div>
<div class="paragraph">
<p>现在准备开始为你的导入器打印出提交数据。
初始信息声明定义了一个提交对象与它所在的分支，紧接着一个你生成的标记、提交者信息与提交信息、然后是一个之前的提交，如果它存在的话。
代码看起来像这样：</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-ruby" data-lang="ruby"># print the import information
puts 'commit refs/heads/master'
puts 'mark :' + mark
puts "committer #{$author} #{date} -0700"
export_data('imported from ' + dir)
puts 'from :' + last_mark if last_mark</code></pre>
</div>
</div>
<div class="paragraph">
<p>我们将硬编码时区信息（-0700），因为这样很容易。
如果从其他系统导入，必须指定为一个偏移的时区。
提交信息必须指定为特殊的格式：</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code>data (size)\n(contents)</code></pre>
</div>
</div>
<div class="paragraph">
<p>这个格式包括文本数据、将要读取数据的大小、一个换行符、最终的数据。
因为之后还需要为文件内容指定相同的数据格式，你需要创建一个帮助函数，<code>export_data</code>：</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-ruby" data-lang="ruby">def export_data(string)
  print "data #{string.size}\n#{string}"
end</code></pre>
</div>
</div>
<div class="paragraph">
<p>剩下的工作就是指定每一个快照的文件内容。
这很轻松，因为每一个目录都是一个快照——可以在目录中的每一个文件内容后打印 <code>deleteall</code> 命令。
Git 将会适当地记录每一个快照：</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-ruby" data-lang="ruby">puts 'deleteall'
Dir.glob("**/*").each do |file|
  next if !File.file?(file)
  inline_data(file)
end</code></pre>
</div>
</div>
<div class="paragraph">
<p>注意：因为大多数系统认为他们的版本是从一个提交变化到另一个提交，fast-import 也可以为每一个提交执行命令来指定哪些文件是添加的、删除的或修改的与新内容是哪些。
可以计算快照间的不同并只提供这些数据，但是这样做会很复杂——也可以把所有数据给 Git 然后让它为你指出来。
如果这更适合你的数据，查阅 <code>fast-import</code> man 帮助页来了解如何以这种方式提供你的数据。</p>
</div>
<div class="paragraph">
<p>这种列出新文件内容或用新内容指定修改文件的格式如同下面的内容：</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code>M 644 inline path/to/file
data (size)
(file contents)</code></pre>
</div>
</div>
<div class="paragraph">
<p>这里，644 是模式（如果你有可执行文件，反而你需要检测并指定 755），inline 表示将会立即把内容放在本行之后。
你的 <code>inline_data</code> 方法看起来像这样：</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-ruby" data-lang="ruby">def inline_data(file, code = 'M', mode = '644')
  content = File.read(file)
  puts "#{code} #{mode} inline #{file}"
  export_data(content)
end</code></pre>
</div>
</div>
<div class="paragraph">
<p>可以重用之前定义的 <code>export_data</code> 方法，因为它与你定义的提交信息数据的方法一样。</p>
</div>
<div class="paragraph">
<p>最后一件你需要做的是返回当前的标记以便它可以传给下一个迭代：</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-ruby" data-lang="ruby">return mark</code></pre>
</div>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<div class="title">Note</div>
</td>
<td class="content">
<div class="paragraph">
<p>如果在 Windows 上还需要确保增加一个额外步骤。
正如之前提到的，Windows 使用 CRLF 作为换行符而 <code>git fast-import</code> 只接受 LF。
为了修正这个问题使 <code>git fast-import</code> 正常工作，你需要告诉 ruby 使用 LF 代替 CRLF：</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-ruby" data-lang="ruby">$stdout.binmode</code></pre>
</div>
</div>
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>就是这样。
这是全部的脚本：</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-ruby" data-lang="ruby">#!/usr/bin/env ruby

$stdout.binmode
$author = "John Doe &lt;john@example.com&gt;"

$marks = []
def convert_dir_to_mark(dir)
    if !$marks.include?(dir)
        $marks &lt;&lt; dir
    end
    ($marks.index(dir)+1).to_s
end

def convert_dir_to_date(dir)
    if dir == 'current'
        return Time.now().to_i
    else
        dir = dir.gsub('back_', '')
        (year, month, day) = dir.split('_')
        return Time.local(year, month, day).to_i
    end
end

def export_data(string)
    print "data #{string.size}\n#{string}"
end

def inline_data(file, code='M', mode='644')
    content = File.read(file)
    puts "#{code} #{mode} inline #{file}"
    export_data(content)
end

def print_export(dir, last_mark)
    date = convert_dir_to_date(dir)
    mark = convert_dir_to_mark(dir)

    puts 'commit refs/heads/master'
    puts "mark :#{mark}"
    puts "committer #{$author} #{date} -0700"
    export_data("imported from #{dir}")
    puts "from :#{last_mark}" if last_mark

    puts 'deleteall'
    Dir.glob("**/*").each do |file|
        next if !File.file?(file)
        inline_data(file)
    end
    mark
end

# Loop through the directories
last_mark = nil
Dir.chdir(ARGV[0]) do
    Dir.glob("*").each do |dir|
        next if File.file?(dir)

        # move into the target directory
        Dir.chdir(dir) do
            last_mark = print_export(dir, last_mark)
        end
    end
end</code></pre>
</div>
</div>
<div class="paragraph">
<p>如果运行这个脚本，你会得到类似下面的内容：</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-console" data-lang="console">$ ruby import.rb /opt/import_from
commit refs/heads/master
mark :1
committer John Doe &lt;john@example.com&gt; 1388649600 -0700
data 29
imported from back_2014_01_02deleteall
M 644 inline README.md
data 28
# Hello

This is my readme.
commit refs/heads/master
mark :2
committer John Doe &lt;john@example.com&gt; 1388822400 -0700
data 29
imported from back_2014_01_04from :1
deleteall
M 644 inline main.rb
data 34
#!/bin/env ruby

puts "Hey there"
M 644 inline README.md
(...)</code></pre>
</div>
</div>
<div class="paragraph">
<p>为了运行导入器，将这些输出用管道重定向到你想要导入的 Git 目录中的 <code>git fast-import</code>。
可以创建一个新的目录并在其中运行 <code>git init</code> 作为开始，然后运行你的脚本：</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-console" data-lang="console">$ git init
Initialized empty Git repository in /opt/import_to/.git/
$ ruby import.rb /opt/import_from | git fast-import
git-fast-import statistics:
---------------------------------------------------------------------
Alloc'd objects:       5000
Total objects:           13 (         6 duplicates                  )
      blobs  :            5 (         4 duplicates          3 deltas of          5 attempts)
      trees  :            4 (         1 duplicates          0 deltas of          4 attempts)
      commits:            4 (         1 duplicates          0 deltas of          0 attempts)
      tags   :            0 (         0 duplicates          0 deltas of          0 attempts)
Total branches:           1 (         1 loads     )
      marks:           1024 (         5 unique    )
      atoms:              2
Memory total:          2344 KiB
       pools:          2110 KiB
     objects:           234 KiB
---------------------------------------------------------------------
pack_report: getpagesize()            =       4096
pack_report: core.packedGitWindowSize = 1073741824
pack_report: core.packedGitLimit      = 8589934592
pack_report: pack_used_ctr            =         10
pack_report: pack_mmap_calls          =          5
pack_report: pack_open_windows        =          2 /          2
pack_report: pack_mapped              =       1457 /       1457
---------------------------------------------------------------------</code></pre>
</div>
</div>
<div class="paragraph">
<p>正如你所看到的，当它成功完成时，它会给你一串关于它完成内容的统计。
这本例中，一共导入了 13 个对象、4 次提交到 1 个分支。
现在，可以运行 <code>git log</code> 来看一下你的新历史：</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-console" data-lang="console">$ git log -2
commit 3caa046d4aac682a55867132ccdfbe0d3fdee498
Author: John Doe &lt;john@example.com&gt;
Date:   Tue Jul 29 19:39:04 2014 -0700

    imported from current

commit 4afc2b945d0d3c8cd00556fbe2e8224569dc9def
Author: John Doe &lt;john@example.com&gt;
Date:   Mon Feb 3 01:00:00 2014 -0700

    imported from back_2014_02_03</code></pre>
</div>
</div>
<div class="paragraph">
<p>做得很好——一个漂亮、干净的 Git 仓库。
要注意的一点是并没有检出任何东西——一开始你的工作目录内并没有任何文件。
为了得到他们，你必须将分支重置到 <code>master</code> 所在的地方：</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-console" data-lang="console">$ ls
$ git reset --hard master
HEAD is now at 3caa046 imported from current
$ ls
README.md main.rb</code></pre>
</div>
</div>
<div class="paragraph">
<p>可以通过 <code>fast-import</code> 工具做很多事情——处理不同模式、二进制数据、多个分支与合并、标签、进度指示等等。
一些更复杂情形下的例子可以在 Git 源代码目录中的 <code>contrib/fast-import</code> 目录中找到。</p>
</div>
</div>
<div id="nav"><a href="/book/zh/v2/Git-%E4%B8%8E%E5%85%B6%E4%BB%96%E7%B3%BB%E7%BB%9F-%E4%BD%9C%E4%B8%BA%E5%AE%A2%E6%88%B7%E7%AB%AF%E7%9A%84-Git">prev</a> | <a href="/book/zh/v2/Git-%E4%B8%8E%E5%85%B6%E4%BB%96%E7%B3%BB%E7%BB%9F-%E6%80%BB%E7%BB%93">next</a></div></div>
</div>

        </div>
      </div>
      <footer>
  <div class="site-source">
    <a href="/site">About this site</a><br>
    Patches, suggestions, and comments are welcome.
  </div>
  <div class="sfc-member">
    Git is a member of <a href="/sfc">Software Freedom Conservancy</a>
  </div>
</footer>
<a href="#top" class="no-js scrollToTop" id="scrollToTop" data-label="Scroll to top">
  <img src="/images/icons/chevron-up@2x.png" width="20" height="20" alt="scroll-to-top"/>
</a>
<script src="/assets/application-b09f91f7c527919e4bc194769429065537d64a7f73b9fe93b107afef32b02939.js"></script>

    </div>

</body>
</html>
